

package SSF.OS;

import com.renesys.raceway.SSF.*; // your implementation of Java SSF here

/** Efficient timers for SSF simulations.  A timer is an entity that
*   wraps itself in a TimerEvent (inner class), and sends itself to itself 
*   on a local channel with the appropriate timeout delay.  When the 
*   delivery occurs, it checks to see whether it has been cancelled; if not, 
*   it executes its own callback method.<p>   For efficiency, all timers for 
*   a given entity use the same loopback channel and process; in an entity 
*   that uses multiple timers, all but the first are "lightweight" timers 
*   that use the initial timer's loopback mechanism.
*/
public abstract class Timer extends Entity {

/** Cache for existing Timer entities */
  private static java.util.Hashtable timer_for_entity = 
    new java.util.Hashtable();

/** Inner class ("Timer as Event") to make up for the fact that Java's  
*   single-inheritance won't let us build a Timer that's both an Entity 
*   and an Event.
*/
  class TimerEvent extends Event {
    public Timer timer;
    public TimerEvent(Timer t0) {
      timer=t0;
    }
  }

/** Determine whether this timer has been cancelled. */
  public boolean isCancelled() { return(expiration<0); };

/** Action to be taken upon expiration if the timer has not been cancelled. */
  public abstract void callback();

/** Expiration date for this timer */
  private long expiration =-1;

/** Number of ticks between timer set and timer expiration */
  private long delay =0;

/** Private loopback channel endpoint. */
  private inChannel fromSelf;

/** Private loopback channel endpoint. */
  private outChannel toSelf;

/** Timer to which this timer defers for its delivery mechanism; null if 
*   this Timer is the first defined for its Entity, and is therefore not 
*   lightweight. 
*/ 
  private Timer chainedTo;

/** Set this timer. */  
  public void set() {
    expiration = now()+delay;
    set(this);
  }

/** Set this timer to expire after the given delay. */  
  public void set(long dt) {
    delay = dt;
    expiration = now()+delay;
    set(this);
  }

/** Set the specified timer. */
  private void set(Timer t) {
    if (chainedTo!=null) 
      chainedTo.set(t);
    else
      toSelf.write(new TimerEvent(t),t.delay);
  }

/** Build a timer for the specified Entity with the specified duration.  
  * After the specified duration has expired (as measured at the Entity), 
  * if the timer's isCancelled() method returns false and the cancel() 
  * method has not been called, then execute the timer's callback() method.
  * <b>Note that you must call set() after creating a timer in order to 
  *  start it.</b>
  * <p>
@param e Entity on whose behalf this Timer fires.
@param dt Duration (in logical ticks) before expiration of the timer.
*/
  public Timer(ProtocolGraph e, long dt) {
    alignTo(e);
    delay = dt;
    if (null == (chainedTo = (Timer)(timer_for_entity.get(e)))){
      timer_for_entity.put(e,this);
      fromSelf = new inChannel(this);
      toSelf = new outChannel(this);
      toSelf.mapto(fromSelf);
      process tproc = new SimpleProcess(this) {
	public void action() {
	  /*SSF.*/Event[] evts = fromSelf.activeEvents();
	  for (int n=0; n<evts.length; n++) 
	    if ((!((TimerEvent)evts[n]).timer.isCancelled()) && 
		((TimerEvent)evts[n]).timer.expiration==now())
	      ((TimerEvent)evts[n]).timer.callback();
	  waitOn(fromSelf);
	}
      };
    } 
  }

/** Specify that the callback event for this timer should not be executed 
*   when it expires. 
*/
  public final void cancel() {
    expiration = -1; 
  }
}


/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
